﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using M7;
using System.IO;
using System.Windows;

namespace S7
{
    public class TcpServer
    {
        private bool IsRunning = false;

        public TcpListener listner = new TcpListener(IPAddress.Any, Info.currentPort);

        public Queue<Msg> SendingQueue = new Queue<Msg>();

        public int NumberOfCustomters = 0;

        public Thread thrRead;
        public Thread thrListen;
        public Thread thrSend;

        public void Start()
        {
            IsRunning = true;
            thrListen = new Thread(new ThreadStart(Listen));
            thrSend = new Thread(new ThreadStart(Send));
            thrSend.IsBackground = true;
            thrListen.IsBackground = true;
            thrListen.Start();
            thrSend.Start();

            Thread thrTables = new Thread(new ThreadStart(CheckTables));
            thrTables.IsBackground = true;
            thrTables.Start();
        }
        private void Listen()
        {
            listner.Start();
            while (IsRunning)
            {
                if (listner.Pending())
                {
                    TcpClient client = listner.AcceptTcpClient();
                    //Thread thr = new Thread(delegate() { GetMsg(client); });
                    //thr.IsBackground = true;
                    //thr.Start();
                    GetMsg(client);
                }
                else
                {
                    Thread.Sleep(50);
                }
            }
        }

        public void Stop()
        {
            IsRunning = false;
            if (thrRead.IsAlive)
            {
                thrRead.Abort();
            }
        }

        private void IntializeConnection(TableControl table, Msg msg)
        {
            table.Connection = new ClientConnection();
            table.Connection.thrRead = new Thread(new ThreadStart(table.Connection.ReadClass));
            table.Connection.thrRead.IsBackground = true;
            table.Connection.current = (CurrentInfo)msg.obj;
            table.IsConnected = true;
            table.Connection.thrRead.Start();
        }

        private void GetMsg(TcpClient client)
        {
            Msg msg = new Msg();
            byte[] b = new byte[4];
            int read = client.Client.Receive(b);
            int datasize = BitConverter.ToInt32(b, 0);
            byte[] buffer = new byte[datasize];
            read = client.Client.Receive(buffer);
            client.Close();
            MemoryStream ms = new MemoryStream(buffer);
            msg = (Msg)Serialization.DeSerialize(ms);

            Info.window.Dispatcher.Invoke(new ThreadStart(() =>
                    {
                        var tables = from table in Info.window.wrpLiveView.Children.OfType<TableControl>()
                                     where table.TableNumber.Equals(msg.TableID.ToString())
                                     select table;

                        if (tables.ToList().Count != 0) //Already known table
                        {
                            TableControl table = tables.ToList()[0];
                            if (msg.enumMethod is enumConnection && (enumConnection)msg.enumMethod == enumConnection.ConnectionConnect)
                            {
                                IntializeConnection(table, msg);
                            }
                            if (table.IsConnected)
                            {
                                table.Connection.ReadingQueue.Enqueue(msg);
                            }
                        }
                        else if (msg.enumMethod is enumConnection && (enumConnection)msg.enumMethod == enumConnection.ConnectionConnect)
                        {
                            //Unknown table
                            if (Table.lstTables == null)
                            {
                                Table.lstTables = Table.Search();
                            }
                            var IPTablesQuery = from table in Table.lstTables
                                                where table.IP.Equals(msg.IP.ToString())
                                                select table;
                            List<Table> IPTables = IPTablesQuery.ToList();

                            if (!IPTables.Count.Equals(0)) //IP is recognized
                            {
                                Msg SendMsg = new Msg();
                                SendMsg.IP = msg.IP;
                                SendMsg.port = msg.port;
                                SendMsg.enumMethod = enumConnection.ConnectionConnect;
                                SendMsg.Succeeded = false;
                                SendMsg.obj = IPTables[0].TableID;
                                Info.server.SendingQueue.Enqueue(SendMsg);
                            }
                            else //IP is not recognized
                            {
                                var NoIPTablesQuery = from table in Table.lstTables
                                                      where table.IP == "0"
                                                      select table;

                                List<Table> NoIPTables = NoIPTablesQuery.ToList();

                                if (!NoIPTables.Count.Equals(0)) //One table has no IP
                                {
                                    Msg SendMsg = new Msg();
                                    SendMsg.IP = msg.IP;
                                    SendMsg.port = msg.port;
                                    SendMsg.enumMethod = enumConnection.ConnectionConnect;
                                    SendMsg.Succeeded = false;
                                    SendMsg.obj = NoIPTables[0].TableID;
                                    Info.server.SendingQueue.Enqueue(SendMsg);
                                }
                                else //All Tables have IPs
                                {
                                    Table table = new Table();
                                    table.IP = msg.IP.ToString();
                                    table.Port = msg.port;
                                    table.TableID = table.Add().Value;
                                    Table.lstTables = Table.Search();
                                        
                                    TableControl newControl = new TableControl();
                                    newControl.TableNumber = table.TableID.ToString();
                                    Info.window.wrpLiveView.Children.Add(newControl);


                                    Msg SendMsg = new Msg();
                                    SendMsg.IP = msg.IP;
                                    SendMsg.port = msg.port;
                                    SendMsg.enumMethod = enumConnection.ConnectionConnect;
                                    SendMsg.Succeeded = false;
                                    SendMsg.obj = table.TableID;
                                    Info.server.SendingQueue.Enqueue(SendMsg);
                                }
                            }
                        }
                    }));
        }

        private void Send()
        {
            while (true)
            {
                if (SendingQueue.Count != 0)
                {
                    Msg SendMsg;
                    lock (SendingQueue)
                    {
                       SendMsg = SendingQueue.Dequeue();
                    }
                    MemoryStream ms = new MemoryStream();
                    ms = Serialization.Serialize(SendMsg);
                    byte[] bo = ms.ToArray();
                    int size = bo.Length;
                    List<byte> lstBytes = bo.ToList();
                    lstBytes.InsertRange(0, BitConverter.GetBytes(size).ToList());
                    byte[] b = lstBytes.ToArray();
                    TcpClient client = new TcpClient();
                    try
                    {
                        client.SendBufferSize = b.Length;
                        client.Connect(SendMsg.IP, SendMsg.port);
                        client.Client.Send(b);
                        client.Close();
                    }
                    catch (SocketException)
                    {
                        //if (con.ContainsKey(SendMsg.IP))
                        //{
                        //    con[SendMsg.IP].Disconnect();
                        //}
                    }
                    
                    if (SendMsg.enumMethod is enumAck)
                    {
                        //Don't Wait for an Ack
                    }
                }
                else
                {
                    //No Msg to Send
                    Thread.Sleep(100);
                }
            }
        }

        private void CheckTables()
        {
            while (true)
            {
                if (Table.lstTables == null)
                {
                    Thread.Sleep(50);
                }
                else
                {
                    foreach (var table in Table.lstTables)
                    {
                        if (table.IP != null || table.Port != null)
                        {
                            Msg SendMsg = new Msg();
                            SendMsg.IP = IPAddress.Parse(table.IP);
                            SendMsg.port = table.Port.Value;
                            SendMsg.enumMethod = enumConnection.ConnectionConnect;
                            SendMsg.obj = table.TableID;
                            SendMsg.Succeeded = false;
                            SendingQueue.Enqueue(SendMsg);
                        }
                    }
                    break;
                }
            }
        }
    }
}
